#include "netlib/base/async_log.h"

#include <memory>

// #include "netlib/base/log_file.h"

using std::make_unique;

netlib::AsyncLog::AsyncLog(double interval) :
    cur_buf_(make_unique<Buffer>()), next_buf_(make_unique<Buffer>()), interval_(interval),
    write_thread_([this] { ThreadFunc(); }, "async log write thread") {}

void netlib::AsyncLog::InitLogFile(const std::string& basename,
                                   const std::string& dirname,
                                   int check_lines,
                                   time_t interval,
                                   off_t max_bytes,
                                   bool is_thread_safe) {
	log_file_ = std::make_unique<LogFile>(basename, dirname, check_lines, interval, max_bytes,
	                                      is_thread_safe);
	write_thread_.Start();
}

void netlib::AsyncLog::Append(const char* data, size_t len) {
	if (cur_buf_->WritableSize() < len) {
		buffers_.push_back(std::move(cur_buf_));
		if (next_buf_) {
			// cur_buf_.reset(next_buf_.release());
			cur_buf_ = std::move(next_buf_);
		} else {
			cur_buf_ = std::make_unique<Buffer>();
		}
		cur_buf_->Append(data, len);
		cond_.Notify();
	} else {
		cur_buf_->Append(data, len);
	}
}

void netlib::AsyncLog::ThreadFunc() {
	BufferPtr buffer1(make_unique<Buffer>());
	BufferPtr buffer2(make_unique<Buffer>());
	std::vector<BufferPtr> to_write_buffers;
	while (true) {
		{
			MutexLockGuard lock(mutex_);
			if (buffers_.empty()) {
				cond_.WaitForSeconds(interval_);
			}
			buffers_.push_back(std::move(cur_buf_));
			cur_buf_ = std::move(buffer1);
			to_write_buffers.swap(buffers_);
			if (!next_buf_) {
				next_buf_ = std::move(buffer2);
			}
		}
		WriteToFile(to_write_buffers);

		if (to_write_buffers.size() > 2) {
			to_write_buffers.resize(2);
		}
		assert(!buffer1);
		assert(!to_write_buffers.empty());
		buffer1 = std::move(to_write_buffers.back());
		buffer1->Reset();
		to_write_buffers.pop_back();
		if (!buffer2) {
			assert(!to_write_buffers.empty());
			buffer2 = std::move(to_write_buffers.back());
			buffer2->Reset();
			to_write_buffers.pop_back();
		}
		log_file_->Flush();
	}
}

void netlib::AsyncLog::WriteToFile(const BufferVector& buffers) {
	if (log_file_) {
		for (const BufferPtr& buffer : buffers) {
			if (buffer->Size()) {
				log_file_->Append(buffer->Data(), buffer->Size());
			}
		}
	}
}